<html><head>

<script type="text/javascript">

function textLimit() {
	var _in = document.getElementById('in');
	var _limit = document.getElementById('limit');
	if (!_in || !limit)
		return "";
	
	var _out = document.getElementById('out')
	_out.value = "";
	_out.value = validate(_in.value, parseInt(_limit.value));
	
}

function validate(text, size) {
	var i;
	var tags = [];
	var STATES = {
		"TEXT"            : 0,
		"TAG_START"       : 1,  // <Tag>...</tag>
		"TAG_SLASH_BEGIN" : 2,  // <tag>...</Tag>
		"TAG_NAME_OPEN"   : 3,  // TAG
		"TAG_NAME_CLOSE"  : 4,  // /TAG
		"TAG_OPT"         : 5,  // <tag OPTS>
		"TAG_SLASH_END"   : 6,  // <taG/>
		"TAG_END_OPEN"    : 7,  // <tag>...</T..>
		"TAG_END_CLOSE"   : 8,  // <tag>...</..G>
	};

	var TAGS = [ "B", "BIG", "DIV", "FONT", "I", "H1", "H2", "H3", "H4", "H5", "P", "S", "SMALL", "SPAN", "STRIKE", "STRONG", "SUB", "SUP", "U" ];

	var state = STATES.TEXT;
	var start = -1;
	var result = "";
	var buf = "";
	var c = "";

	for (i=0; i < text.length && size > 0; i++) { //>
		c = text.substr(i, 1);
		var tag = "";
		
		// change state
		switch (state) {
			case STATES.TEXT:
				if (c == '<') { //>
					state = STATES.TAG_START;
				}
				break;
				
			case STATES.TAG_START:
				if (c == '/') {
					state = STATES.TAG_SLASH_BEGIN;
				} else if (c.toLowerCase() >= 'a' && c.toLowerCase() <= 'z') { //>
					state = STATES.TAG_NAME_OPEN;
					start = i;
				} else {
					state = STATES.TEXT;
				}
				break;
				
			case STATES.TAG_SLASH_BEGIN:
				if (c == '>') {
					state = STATES.TEXT;
				} else {
					state = STATES.TAG_NAME_CLOSE;
					start = i;
				}
				break;
				
			case STATES.TAG_NAME_OPEN:
				if (c == '/') {
					state = STATES.TAG_SLASH_END;
					tag = text.substr(start, i - start).toUpperCase();
				} else if (c == '>') {
					state = STATES.TAG_END_OPEN;
					tag = text.substr(start, i - start).toUpperCase();
				} else if (c == ' ') {
					state = STATES.TAG_OPT;
					tag = text.substr(start, i - start).toUpperCase();
				} else if (c == '<') { //>
					state = STATES.TAG_START;
					var str = text.substr(start, i - start).substr(0, size);
					buf  += str;
					size -= str.length;
					start = -1;
					break;
				}
				break;
				
			case STATES.TAG_NAME_CLOSE:
				if (c == '>') {
					state = STATES.TAG_END_CLOSE;
					tag = text.substr(start, i - start).toUpperCase();
				} else if (c == ' ') {
					tag = text.substr(start, i - start).toUpperCase();
				}
				break;
				
			case STATES.TAG_OPT:
				if (c == '>') {
					state = STATES.TAG_END_OPEN;
				} else if (c == '/') {
					state = STATES.TAG_SLASH_END;
				} else if (c == '<') { //>
					state = STATES.TAG_START;
					tags.pop();
					buf   = buf.substr(0, size);
					size -= buf.length;
				}
				break;
				
			case STATES.TAG_SLASH_END:
				if (c == '>') {
					state = STATES.TAG_END_OPEN;
				} else {
					state = STATES.TEXT;
					buf   = buf.substr(0, size);
					size -= buf.length;
				}
				break;
				
			case STATES.TAG_END_OPEN:
				if (c == '<') { //>
					state = STATES.TAG_START;
				} else {
					state = STATES.TEXT;
				}
				break;
				
			case STATES.TAG_END_CLOSE:
				if (c == '<') { //>
					state = STATES.TAG_START;
				} else {
					state = STATES.TEXT;
				}
				break;
		}
		
		
		switch (state) {
		
			case STATES.TEXT:
				// decrease counter
				size -= 1;
				result += buf;
				buf = "";
				break;
		    
			case STATES.TAG_START:
				result += buf;
				buf = "";
				break;
		
			default:
				break;
		}
		
		
		// store text to buf 
		if (start == -1) {
			buf += c;
		}

		if (!tag)
			continue;

TAG_ALLOW:
		{
		    for (t in TAGS) {
			if (!TAGS.hasOwnProperty(t))
				contnue;
			if (TAGS[t] == tag)
				break TAG_ALLOW;
		    }

		    // not found
		    buf += tag;
		    buf += c;
		    start = -1;
		    continue;
		}
		


		// found
		switch (state) {
		
			case STATES.TAG_NAME_OPEN:
			case STATES.TAG_END_OPEN:
			case STATES.TAG_OPT:
			        // push tag
				tags.push(tag);
				buf += tag;
				buf += c;
				break;
				
			case STATES.TAG_NAME_CLOSE:
			case STATES.TAG_END_CLOSE:
				// search tag in stack
TAG_FOUND_IN_STACK:
				{
					for (t in tags) {
						if (!tags.hasOwnProperty(t))
							continue;
						if (tags[t] == tag) {
							break TAG_FOUND_IN_STACK;
						}
					}
					// clear buffer
					buf = "";
					break; // from switch
				}
				
				
				// pop tags
				while (tags.length) {
					var tmp = tags.pop();
					buf += tmp;
					if (tmp != tag) {
						buf += "></"; //>
						continue;
					}
					break;
				}
				buf += c;
				break;
				
			default:
				break;
		}
		
		start = -1;
		
	}

	// fix limit by string length	
	if (size && start != -1) {
		buf += c;
	}
		
	// put buf
	result += buf.substr(0, size);
	
	// get left tags from stack
	while (tags.length)
		result += "</" + tags.pop() + ">";

	return result;
}
</script>
</head><body>

in:&nbsp;&nbsp;&nbsp;&nbsp;<input type="text" name="in" id="in" style="width: 500px;" value="a <p class='err'><br/>123 <b>4 12</font>3 1245546778"/>
<br/>
limit: <input type="text" name="limit" id="limit" style="width:40px;" value="8"/>
<br/>
out:&nbsp;&nbsp;<input type="text" name="text" id="out" style="width: 500px;" value=""/>
<br/>
<button value="Ok" onClick="textLimit();">OK</button>
<br />

</body></html>
